%language "c++"
%define api.namespace {yy}
%define api.parser.class {Parser}
%define api.value.type variant
%define api.token.constructor
%define parse.assert

%locations

%param        { Front & front }

%define parse.trace
%define parse.error detailed
%define parse.lac full

%code requires {
#include <common.h>
#include <ast/ast.h>
#include <stack_map.h>
class Front;
extern StackMap<std::string, int> const_int_stack;
}

%code {
#include <parser/front.h>
StackMap<std::string, int> const_int_stack;
}

%define api.token.prefix {TOK_}

%token <int> INT_NUMBER
%token <float> FLOAT_NUMBER
%token <std::string> IDEN

%token INT
%token FLOAT
%token VOID
%token CONST

%token RETURN
%token IF
%right THEN ELSE
%token WHILE
%token BREAK
%token CONTINUE

%token SEMICOLON
%token COMMA
%token EQ

// %left OR AND
%left EEQ NEQ
// %left G GE L LE
%left ADD SUB
%left MUL DIV REM

%token LB_L RB_L
%token LB_M RB_M
%token LB_H RB_H

%start CompUnit
%nterm <VarType>                                        BType
%nterm <std::unique_ptr<ast::NodeGlobalDecl>>           GlobalDecl
%nterm <std::unique_ptr<ast::NodeGlobalDefs>>           GlobalDefs
%nterm <std::unique_ptr<ast::NodeGlobalDef>>            GlobalDef
%nterm <std::unique_ptr<ast::NodeConstDecl>>            ConstDecl
%nterm <std::unique_ptr<ast::NodeConstDefs>>            ConstDefs
%nterm <std::unique_ptr<ast::NodeConstDef>>             ConstDef
%nterm <std::unique_ptr<ast::NodeVarDecl>>              VarDecl
%nterm <std::unique_ptr<ast::NodeVarDefs>>              VarDefs
%nterm <std::unique_ptr<ast::NodeVarDef>>               VarDef
%nterm <std::unique_ptr<ast::NodeArrayConst>>           ArrayConst
%nterm <std::unique_ptr<ast::NodeArray>>                Array
%nterm <std::unique_ptr<ast::NodeArrayInit>>            ArrayInit
%nterm <std::unique_ptr<ast::NodeFuncDef>>              FuncDef
%nterm <std::unique_ptr<ast::NodeFuncDefArgs>>          FuncDefArgs
%nterm <std::unique_ptr<ast::NodeFuncCall>>             FuncCall
%nterm <std::unique_ptr<ast::NodeBlock>>                Block
%nterm <std::unique_ptr<ast::NodeBlockItems>>           BlockItems
%nterm <std::unique_ptr<ast::NodeStmt>>                 Stmt
%nterm <std::unique_ptr<ast::NodeStmtAssign>>           StmtAssign
%nterm <std::unique_ptr<ast::NodeStmtReturn>>           StmtReturn
%nterm <std::unique_ptr<ast::NodeStmtIf>>               StmtIf
%nterm <std::unique_ptr<ast::NodeStmtWhile>>            StmtWhile
%nterm <std::unique_ptr<ast::NodeStmtBreak>>            StmtBreak
%nterm <std::unique_ptr<ast::NodeStmtContinue>>         StmtContinue
%nterm <std::unique_ptr<ast::NodeStmtFuncCall>>         StmtFuncCall
%nterm <std::unique_ptr<ast::NodeExp>>                  Exp
%nterm <std::unique_ptr<ast::NodeFuncArgs>>             FuncArgs
%nterm <std::unique_ptr<ast::NodeCond>>                 Cond
%nterm <int>                                            ConstIntExp

%%

CompUnit:
        GlobalDecl {
          front.result = std::make_unique<ast::NodeCompUnit>();
          front.result->global_list.push_back(std::move($1));
        } |
        FuncDef {
          front.result = std::make_unique<ast::NodeCompUnit>();
          front.result->global_list.push_back(std::move($1));
        } |
        CompUnit GlobalDecl {
          front.result->global_list.push_back(std::move($2));
        } |
        CompUnit FuncDef {
          front.result->global_list.push_back(std::move($2));
        };

BType:
     INT {
      $$ = VarType::i32;
     } |
     FLOAT {
      $$ = VarType::f32;
     } |
     VOID {
      $$ = VarType::none;
     };

GlobalDecl:
          BType GlobalDefs SEMICOLON {
            $$ = std::make_unique<ast::NodeGlobalDecl>();
            $$->type = $1;
            $$->global_defs = std::move($2);
          };

GlobalDefs:
          GlobalDefs COMMA GlobalDef {
            $$ = std::move($1);
            $$->global_def_list.push_back(std::move($3));
          } |
          GlobalDef {
            $$ = std::make_unique<ast::NodeGlobalDefs>();
            $$->global_def_list.push_back(std::move($1));
          };

GlobalDef:
         IDEN EQ ConstIntExp {
          $$ = std::make_unique<ast::NodeGlobalDef>();
          $$->iden = $1;
          $$->int_number = $3;
         } |
         IDEN {
          $$ = std::make_unique<ast::NodeGlobalDef>();
          $$->iden = $1;
          $$->int_number = 0;
         };

ConstDecl:
         CONST BType ConstDefs SEMICOLON {
          $$ = std::make_unique<ast::NodeConstDecl>();
          $$->type = $2;
          $$->const_defs = std::move($3);
         };

ConstDefs:
         ConstDefs COMMA ConstDef {
          $$ = std::move($1);
          $$->const_def_list.push_back(std::move($3));
         } |
         ConstDef {
          $$ = std::make_unique<ast::NodeConstDefs>();
          $$->const_def_list.push_back(std::move($1));
         };

ConstDef:
        IDEN EQ ConstIntExp {
          $$ = std::make_unique<ast::NodeConstDef>();
          $$->iden = $1;
          $$->int_number = $3;
          const_int_stack.push($1, $3);
        };

VarDecl:
       BType VarDefs SEMICOLON {
        $$ = std::make_unique<ast::NodeVarDecl>();
        $$->type = $1;
        $$->var_defs = std::move($2);
       };

VarDefs:
       VarDefs COMMA VarDef {
        $$ = std::move($1);
        $$->var_def_list.push_back(std::move($3));
       } |
       VarDef {
        $$ = std::make_unique<ast::NodeVarDefs>();
        $$->var_def_list.push_back(std::move($1));
       };

VarDef:
      IDEN {
        $$ = std::make_unique<ast::NodeVarDef>(ast::NodeVarDef::Stage::EXP_NOT_INIT);
        $$->iden = $1;
      } |
      IDEN EQ Exp {
        $$ = std::make_unique<ast::NodeVarDef>(ast::NodeVarDef::Stage::EXP_INIT);
        $$->iden = $1;
        $$->exp = std::move($3);
      } |
      IDEN ArrayConst {
        $$ = std::make_unique<ast::NodeVarDef>(ast::NodeVarDef::Stage::ARR_NOT_INIT);
        $$->iden = $1;
        $$->array_const = std::move($2);
      } |
      IDEN ArrayConst EQ LB_H RB_H {
        $$ = std::make_unique<ast::NodeVarDef>(ast::NodeVarDef::Stage::ARR_ZERO_INIT);
        $$->iden = $1;
        $$->array_const = std::move($2);
      } |
      IDEN ArrayConst EQ LB_H ArrayInit RB_H {
        $$ = std::make_unique<ast::NodeVarDef>(ast::NodeVarDef::Stage::ARR_INIT);
        $$->iden = $1;
        $$->array_const = std::move($2);
        $$->array_init = std::move($5);
      };

ArrayConst:
     LB_M ConstIntExp RB_M {
      $$ = std::make_unique<ast::NodeArrayConst>();
      $$->int_number_list.push_back($2);
     } |
     ArrayConst LB_M ConstIntExp RB_M {
      $$ = std::move($1);
      $$->int_number_list.push_back($3);
     };

Array:
     LB_M Exp RB_M {
      $$ = std::make_unique<ast::NodeArray>();
      $$->exp_list.push_back(std::move($2));
     } |
     Array LB_M Exp RB_M {
      $$ = std::move($1);
      $$->exp_list.push_back(std::move($3));
     };

ArrayInit:
         LB_H RB_H {
          $$ = std::make_unique<ast::NodeArrayInit>();
         } |
         Exp {
          $$ = std::make_unique<ast::NodeArrayInit>();
          $$->init_list.push_back(std::move($1));
         } |
         LB_H ArrayInit RB_H {
          $$ = std::make_unique<ast::NodeArrayInit>();
          $$->init_list.push_back(std::move($2));
         } |
         ArrayInit COMMA Exp {
          $$ = std::move($1);
          $$->init_list.push_back(std::move($3));
         } |
         ArrayInit COMMA LB_H RB_H {
          $$ = std::move($1);
          $$->init_list.push_back(std::make_unique<ast::NodeArrayInit>());
         } |
         ArrayInit COMMA LB_H ArrayInit RB_H {
          $$ = std::move($1);
          $$->init_list.push_back(std::move($4));
         };

FuncDef:
       BType IDEN LB_L RB_L Block {
        $$ = std::make_unique<ast::NodeFuncDef>(ast::NodeFuncDef::NOT_ARGS);
        $$->type = $1;
        $$->iden = $2;
        $$->block = std::move($5);
       } |
       BType IDEN LB_L FuncDefArgs RB_L Block {
        $$ = std::make_unique<ast::NodeFuncDef>(ast::NodeFuncDef::HAVE_ARGS);
        $$->type = $1;
        $$->iden = $2;
        $$->func_def_args = std::move($4);
        $$->block = std::move($6);
       };

FuncDefArgs:
           BType IDEN {
            $$ = std::make_unique<ast::NodeFuncDefArgs>();
            $$->type_list.push_back($1);
            $$->iden_list.push_back($2);
           } |
           FuncDefArgs COMMA BType IDEN {
            $$ = std::move($1);
            $$->type_list.push_back($3);
            $$->iden_list.push_back($4);
           };

FuncCall:
   IDEN LB_L FuncArgs RB_L {
    $$ = std::make_unique<ast::NodeFuncCall>();
    $$->iden = $1;
    $$->func_args = std::move($3);
   } |
   IDEN LB_L RB_L {
    $$ = std::make_unique<ast::NodeFuncCall>();
    $$->iden = $1;
   };

Block:
     LB_H BlockItems RB_H {
      $$ = std::make_unique<ast::NodeBlock>();
      $$->block_item_list = std::move($2);
     };

BlockItems:
          BlockItems ConstDecl {
            $$ = std::move($1);
            $$->const_decl_list.push_back(std::move($2));
          } |
          BlockItems VarDecl {
            $$ = std::move($1);
            $$->var_decl_list.push_back(std::move($2));
          } |
          BlockItems Stmt {
            $$ = std::move($1);
            $$->stmt_list.push_back(std::move($2));
          } |
          ConstDecl {
            $$ = std::make_unique<ast::NodeBlockItems>();
            $$->const_decl_list.push_back(std::move($1));
          } |
          Stmt {
            $$ = std::make_unique<ast::NodeBlockItems>();
            $$->stmt_list.push_back(std::move($1));
          } |
          VarDecl {
            $$ = std::make_unique<ast::NodeBlockItems>();
            $$->var_decl_list.push_back(std::move($1));
          };

Stmt:
    SEMICOLON {
      // empty stmt
    } |
    StmtAssign {
      $$ = std::make_unique<ast::NodeStmt>(ast::NodeStmt::Stage::ASSIGN);
      $$->stmt_assign = std::move($1);
    } |
    StmtReturn {
      $$ = std::make_unique<ast::NodeStmt>(ast::NodeStmt::Stage::RETURN);
      $$->stmt_return = std::move($1);
    } |
    StmtIf {
      $$ = std::make_unique<ast::NodeStmt>(ast::NodeStmt::Stage::IF);
      $$->stmt_if = std::move($1);
    } |
    StmtWhile {
      $$ = std::make_unique<ast::NodeStmt>(ast::NodeStmt::Stage::WHILE);
      $$->stmt_while = std::move($1);
    } |
    StmtBreak {
      $$ = std::make_unique<ast::NodeStmt>(ast::NodeStmt::Stage::BREAK);
      $$->stmt_break = std::move($1);
    } |
    StmtContinue {
      $$ = std::make_unique<ast::NodeStmt>(ast::NodeStmt::Stage::CONTINUE);
      $$->stmt_continue = std::move($1);
    } |
    StmtFuncCall {
      $$ = std::make_unique<ast::NodeStmt>(ast::NodeStmt::Stage::FUNCCALL);
      $$->stmt_func_call = std::move($1);
    } |
    Block {
      $$ = std::make_unique<ast::NodeStmt>(ast::NodeStmt::Stage::BLOCK);
      $$->block = std::move($1);
    };

StmtAssign:
          IDEN EQ Exp SEMICOLON {
            $$ = std::make_unique<ast::NodeStmtAssign>(ast::NodeStmtAssign::NOARR);
            $$->iden = $1;
            $$->exp = std::move($3);
          } |
          IDEN Array EQ Exp SEMICOLON {
            $$ = std::make_unique<ast::NodeStmtAssign>(ast::NodeStmtAssign::ARRAY);
            $$->iden = $1;
            $$->array = std::move($2);
            $$->exp = std::move($4);
          };

StmtReturn:
          RETURN Exp SEMICOLON {
            $$ = std::make_unique<ast::NodeStmtReturn>();
            $$->exp = std::move($2);
          } |
          RETURN SEMICOLON {
            $$ = std::make_unique<ast::NodeStmtReturn>();
          };

StmtIf:
      IF LB_L Cond RB_L Stmt %prec THEN {
        $$ = std::make_unique<ast::NodeStmtIf>(ast::NodeStmtIf::Stage::NO_ELSE);
        $$->cond = std::move($3);
        $$->stmt_true = std::move($5);
      } |
      IF LB_L Cond RB_L Stmt ELSE Stmt {
        $$ = std::make_unique<ast::NodeStmtIf>(ast::NodeStmtIf::Stage::ELSE);
        $$->cond = std::move($3);
        $$->stmt_true = std::move($5);
        $$->stmt_false = std::move($7);
      };

StmtWhile:
         WHILE LB_L Cond RB_L Stmt {
          $$ = std::make_unique<ast::NodeStmtWhile>();
          $$->cond = std::move($3);
          $$->stmt = std::move($5);
         };

StmtBreak:
         BREAK SEMICOLON {
          $$ = std::make_unique<ast::NodeStmtBreak>();
         };

StmtContinue:
            CONTINUE SEMICOLON {
              $$ = std::make_unique<ast::NodeStmtContinue>();
            };

StmtFuncCall:
            FuncCall SEMICOLON {
              $$ = std::make_unique<ast::NodeStmtFuncCall>();
              $$->func_call = std::move($1);
            };

Exp:
   Exp EEQ Exp {
    $$ = std::make_unique<ast::NodeExp>(ast::NodeExp::EXP);
    $$->exp1 = std::move($1);
    $$->op_type = OpType::EEQ;
    $$->exp2 = std::move($3);
   } |
   Exp NEQ Exp {
    $$ = std::make_unique<ast::NodeExp>(ast::NodeExp::EXP);
    $$->exp1 = std::move($1);
    $$->op_type = OpType::NEQ;
    $$->exp2 = std::move($3);
   } |
   Exp ADD Exp {
    $$ = std::make_unique<ast::NodeExp>(ast::NodeExp::EXP);
    $$->exp1 = std::move($1);
    $$->op_type = OpType::ADD;
    $$->exp2 = std::move($3);
   } |
   Exp SUB Exp {
    $$ = std::make_unique<ast::NodeExp>(ast::NodeExp::EXP);
    $$->exp1 = std::move($1);
    $$->op_type = OpType::SUB;
    $$->exp2 = std::move($3);
   } |
   Exp MUL Exp {
    $$ = std::make_unique<ast::NodeExp>(ast::NodeExp::EXP);
    $$->exp1 = std::move($1);
    $$->op_type = OpType::MUL;
    $$->exp2 = std::move($3);
   } |
   Exp DIV Exp {
    $$ = std::make_unique<ast::NodeExp>(ast::NodeExp::EXP);
    $$->exp1 = std::move($1);
    $$->op_type = OpType::DIV;
    $$->exp2 = std::move($3);
   } |
   Exp REM Exp {
    $$ = std::make_unique<ast::NodeExp>(ast::NodeExp::EXP);
    $$->exp1 = std::move($1);
    $$->op_type = OpType::REM;
    $$->exp2 = std::move($3);
   } |
   LB_L Exp RB_L {
    $$ = std::make_unique<ast::NodeExp>(ast::NodeExp::BRAC_EXP);
    $$->exp1 = std::move($2);
   } |
   INT_NUMBER {
    $$ = std::make_unique<ast::NodeExp>(ast::NodeExp::INT_NUMBER);
    $$->int_number = $1;
   } |
   SUB INT_NUMBER {
    $$ = std::make_unique<ast::NodeExp>(ast::NodeExp::INT_NUMBER);
    $$->int_number = -$2;
   } |
   FLOAT_NUMBER {
    $$ = std::make_unique<ast::NodeExp>(ast::NodeExp::FLOAT_NUMBER);
    $$->float_number = $1;
   } |
   SUB FLOAT_NUMBER {
    $$ = std::make_unique<ast::NodeExp>(ast::NodeExp::FLOAT_NUMBER);
    $$->float_number = -$2;
   } |
   IDEN {
    $$ = std::make_unique<ast::NodeExp>(ast::NodeExp::Stage::IDEN);
    $$->iden = $1;
   } |
   IDEN Array {
    $$ = std::make_unique<ast::NodeExp>(ast::NodeExp::Stage::ARRAY);
    $$->iden = $1;
    $$->array = std::move($2);
   } |
   FuncCall {
    $$ = std::make_unique<ast::NodeExp>(ast::NodeExp::FUNC);
    $$->func_call = std::move($1);
   };

FuncArgs:
        Exp {
          $$ = std::make_unique<ast::NodeFuncArgs>();
          $$->exp_list.push_back(std::move($1));
        } |
        FuncArgs COMMA Exp {
          $$ = std::move($1);
          $$->exp_list.push_back(std::move($3));
        };

Cond:
    Exp {
      $$ = std::make_unique<ast::NodeCond>();
      $$->exp = std::move($1);
    };

ConstIntExp:
           ConstIntExp ADD ConstIntExp {
            $$ = $1 + $3;
           } |
           ConstIntExp SUB ConstIntExp {
            $$ = $1 - $3;
           } |
           ConstIntExp MUL ConstIntExp {
            $$ = $1 * $3;
           } |
           ConstIntExp DIV ConstIntExp {
            $$ = $1 / $3;
           } |
           ConstIntExp REM ConstIntExp {
            $$ = $1 % $3;
           } |
           INT_NUMBER {
            $$ = $1;
           } |
           IDEN {
            if(const_int_stack.find($1) == false)
              error(front.loc, std::string("iden:") + $1 + "not defined");
            $$ = const_int_stack.get($1);
           };

%%

void yy::Parser::error(const location_type & loc, const std::string & msg) {
  std::cerr << loc << ": " << msg << std::endl;
}
